﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;

namespace Tsanie.Image2Cmt {
    class Canvas : PictureBox {
        private static readonly Bitmap _cursorMove = Tsanie.Image2Cmt.Properties.Resources.move;
        private static readonly Cursor _move;
        static Canvas() {
            _move = new Cursor(_cursorMove.GetHicon());
        }

        private Thread _threadCreateBitmap = null;
        private Bitmap _bmp = null;
        private int _offsetWidth = 0, _offsetHeight = 0;
        private int _offsetX = 0, _offsetY = 0;
        private int _offsetLastX = 0, _offsetLastY = 0;
        private Point _lastPoint = Point.Empty;

        public event EventHandler CreateDone;
        public ValueBitmap _bitmap = null;
        [DefaultValue(null)]
        public ValueBitmap Bitmap {
            get { return _bitmap; }
            set {
                _bitmap = value;
                if (_bitmap != null) {
                    if (_threadCreateBitmap != null && _threadCreateBitmap.ThreadState == ThreadState.Running) {
                        _threadCreateBitmap.Abort();
                        _threadCreateBitmap = null;
                    }
                    _threadCreateBitmap = new Thread(() => CreateBitmap()) { Name = "threadCreateBitmap" };
                    _threadCreateBitmap.Start();
                }
            }
        }
        private decimal radius;

        public Canvas()
            : base() {
            SetStyle(ControlStyles.AllPaintingInWmPaint |
                ControlStyles.OptimizedDoubleBuffer,
                true);
            this.ResizeRedraw = true;
        }

        public void Reset() {
            _offsetX = 0;
            _offsetY = 0;
            _offsetWidth = 0;
            _offsetHeight = 0;
        }

        private unsafe void CreateBitmap() {
            if (_bitmap == null || _bitmap.Layers == null)
                return;
            if (_bmp != null)
                _bmp.Dispose();

            //// 创建调色板分布图
            //int total = _bitmap.MostColorCount;
            //int length = (int)Math.Sqrt(_bitmap.Palette.Length);
            //radius = 1.0M;
            //_bmp = new Bitmap(length, length, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            //BitmapData data = _bmp.LockBits(new Rectangle(0, 0, length, length),
            //                                ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
            //byte* p = (byte*)data.Scan0;
            //int strideOut = data.Stride - length * 4;
            //for (int y = 0; y < length; y++) {
            //    for (int x = 0; x < length; x++) {
            //        Histogram<Color> c = _bitmap.Palette[y * length + x];
            //        *p++ = 255;
            //        *p++ = 255;
            //        *p++ = 255;
            //        *p++ = (byte)(c.count * 255 / total);
            //    }
            //    p += strideOut;
            //}
            //_bmp.UnlockBits(data);

            _bmp = new Bitmap(_bitmap.LayerWidth, _bitmap.LayerHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            radius = (decimal)_bitmap.LayerWidth / (decimal)_bitmap.LayerHeight;
            BitmapData data = _bmp.LockBits(new Rectangle(0, 0, _bitmap.LayerWidth, _bitmap.LayerHeight),
                ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
            byte* p = (byte*)data.Scan0;
            foreach (KeyValuePair<Color, ColorLayer> pair in _bitmap.Layers) {
                Color c = pair.Key;
                byte[] bs = new byte[] { c.B, c.G, c.R, c.A };
                List<Line> lines = pair.Value.lines;
                lock (lines) {
                    foreach (Line line in lines) {
                        byte* pt = p + data.Stride * line.index + line.offset * 4;
                        for (int i = line.offset; i < line.end; i++) {
                            *pt++ = bs[0];
                            *pt++ = bs[1];
                            *pt++ = bs[2];
                            *pt++ = bs[3];
                        }
                    }
                }
            }
            _bmp.UnlockBits(data);

            if (CreateDone != null)
                CreateDone(this, EventArgs.Empty);
            this.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs pe) {
            //base.OnPaint(pe);
            if (DesignMode || _bitmap == null || _bmp == null)
                return;
            Graphics g = pe.Graphics;
            g.Clear(this.BackColor);
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
            int widthclient = this.Width;
            int heightclient = this.Height;
            int width = _bitmap.LayerWidth + _offsetWidth;
            int height = _bitmap.LayerHeight + _offsetHeight;
            Rectangle rectDest = new Rectangle(
                (widthclient - width) / 2 + _offsetX,
                (heightclient - height) / 2 + _offsetY,
                width,
                height);
            g.DrawImage(_bmp, rectDest, new Rectangle(Point.Empty, _bmp.Size), GraphicsUnit.Pixel);
        }
        protected override void OnMouseWheel(MouseEventArgs e) {
            if (_bmp == null)
                return;
            _offsetWidth += e.Delta / 4;
            if (_offsetWidth < -_bmp.Width) _offsetWidth = 1 - _bmp.Width;
            _offsetHeight = (int)(_offsetWidth / radius);
            if (_offsetHeight < -_bmp.Height) _offsetHeight = 1 - _bmp.Height;
            this.Invalidate();
        }
        protected override void OnMouseDown(MouseEventArgs e) {
            this.Focus();
            if (_bmp == null)
                return;
            if (e.Button == System.Windows.Forms.MouseButtons.Left) {
                _lastPoint = e.Location;
                _offsetLastX = _offsetX;
                _offsetLastY = _offsetY;
                this.Cursor = _move;
            } else if (e.Button == System.Windows.Forms.MouseButtons.Right) {
                if (e.Clicks == 1) {
                    Reset();
                    this.Invalidate();
                } else if (e.Clicks == 2) {
                    _offsetX = 0;
                    _offsetY = 0;
                    if (radius > (decimal)this.Width / (decimal)this.Height) {
                        // 宽
                        if (_bitmap.LayerWidth > this.Width) {
                            _offsetWidth = this.Width - _bitmap.LayerWidth;
                            _offsetHeight = (int)(this.Width / radius) - _bitmap.LayerHeight;
                        }
                    } else {
                        // 高
                        if (_bitmap.LayerHeight > this.Height) {
                            _offsetHeight = this.Height - _bitmap.LayerHeight;
                            _offsetWidth = (int)(this.Height * radius) - _bitmap.LayerWidth;
                        }
                    }
                    this.Invalidate();
                }
            }
        }
        protected override void OnMouseMove(MouseEventArgs e) {
            if (_bmp == null)
                return;
            if (e.Button == System.Windows.Forms.MouseButtons.Left) {
                _offsetX = _offsetLastX + e.X - _lastPoint.X;
                _offsetY = _offsetLastY + e.Y - _lastPoint.Y;
                this.Invalidate();
            }
        }
        protected override void OnMouseUp(MouseEventArgs e) {
            if (_bmp == null)
                return;
            if (e.Button == System.Windows.Forms.MouseButtons.Left) {
                this.Cursor = Cursors.Default;
            }
        }
    }
}
